#ifndef THREAD_SAFE_QUEUE_H
#define THREAD_SAFE_QUEUE_H

#include <queue>
#include <pthread.h>
#include <assert.h>

using namespace std;

template <typename T>
class Queue : public queue<T>
{
	pthread_mutex_t lock;
	
public:
	Queue();

	void clear();
	void push_back(const T&);
	T pop();
};

template <typename T1, typename T2, typename T3>
class Priority_Queue : public priority_queue<T1, T2, T3>
{
	pthread_mutex_t lock;
	
public:
	Priority_Queue();
	
	void clear();
	void push_back(const T1&);
	T1 pop();
};

template <typename T>
Queue<T>::Queue()
{
	pthread_mutex_init(&lock, NULL);
}

template <typename T>
T Queue<T>::pop()
{
	assert(!queue<T>::empty());
	
	pthread_mutex_lock(&lock);
	T tmp = queue<T>::top();
	queue<T>::pop();
	pthread_mutex_unlock(&lock);
	
	return tmp;
}

template <typename T>
void Queue<T>::push_back(const T& item)
{
	pthread_mutex_lock(&lock);
	queue<T>::push_back(item);
	pthread_mutex_unlock(&lock);
}

template <typename T>
void Queue<T>::clear()
{
	pthread_mutex_lock(&lock);
	queue<T>::clear();
	pthread_mutex_unlock(&lock);
}


template <typename T1, typename T2, typename T3>
Priority_Queue<T1, T2, T3>::Priority_Queue()
{
	pthread_mutex_init(&lock, NULL);
}

template <typename T1, typename T2, typename T3>
T1 Priority_Queue<T1, T2, T3>::pop()
{
	assert(!(priority_queue<T1, T2, T3>::empty()));
	
	pthread_mutex_lock(&lock);
	T1 tmp = priority_queue<T1, T2, T3>::top();
	priority_queue<T1, T2, T3>::pop();
	pthread_mutex_unlock(&lock);
	
	return tmp;
}

template <typename T1, typename T2, typename T3>
void Priority_Queue<T1, T2, T3>::push_back(const T1& item)
{
	pthread_mutex_lock(&lock);
	priority_queue<T1, T2, T3>::push_back(item);
	pthread_mutex_unlock(&lock);
}

template <typename T1, typename T2, typename T3>
void Priority_Queue<T1, T2, T3>::clear()
{
	pthread_mutex_lock(&lock);
	priority_queue<T1, T2, T3>::clear();
	pthread_mutex_unlock(&lock);
}

#endif
